'use strict';
const { Service } = require('egg');
const crypto = require('crypto');
const data = require('../data/redirect_uri');

class TokenService extends Service {
  verifyClientSecret(client_id, client_secret) {
    for (const i in data) {
      if (!data.hasOwnProperty(i)) continue;
      if (data[i].client_id === client_id) {
        return (data[i].client_secret === client_secret);
      }
    }
    return false;
  }

  verifyScope(scope, scopeArray) {
    if (!scope) return true;

    const target = scope.split(' ');
    for (const i in target) {
      if (!target.hasOwnProperty(i)) continue;
      if (scopeArray.indexOf(target[i]) === -1) return false;
    }
    return true;
  }

  async verifyCode(code, client_id, redirect_uri) {
    const { ctx } = this;
    const result = await ctx.model.Code.findByPk(Number(code));
    return !!result
      && result.valid
      && result.client_id === client_id
      && result.redirect_uri === redirect_uri
      && (Date.now() - result.created_at <= 1000 * 5 * 60)
      && Date.now() > result.created_at;
  }

  async getByRefreshToken(refresh) {
    const { ctx } = this;
    return await ctx.model.Token.findOne({ where: { refresh } });
  }

  async getByCode(code) {
    const { ctx } = this;
    return await ctx.model.Code.findByPk(code);
  }

  generateToken(obj) {
    obj.token_created_at = Date.now();
    let str = JSON.stringify(obj);

    let random = Math.random().toString();
    let hmac = crypto.createHmac('sha256', random);
    hmac.update(str);
    const token = hmac.digest('base64');

    random = Math.random().toString();
    hmac = crypto.createHmac('sha256', random);
    obj.token = token;
    str = JSON.stringify(obj);
    hmac.update(str);
    const refresh = hmac.digest('base64');

    return { token, refresh };
  }

  async grant(code) {
    const { ctx } = this;
    const result = await ctx.model.Code.findByPk(Number(code));
    await result.update({ valid: false });
    const { token, refresh } = this.generateToken(result);

    const tokenResult = await ctx.model.Token.create({
      token,
      refresh,
      code,
      valid: true,
    });

    if (tokenResult) {
      return {
        access_token: token,
        token_type: 'bearer',
        expires_in: 5 * 60,
        refresh_token: refresh,
        scope: JSON.parse(result.scope),
      };
    }
    return null;
  }

  async refresh(token, scope) {
    const { ctx } = this;
    await token.update({ valid: false });
    const tokens = this.generateToken(token);
    const new_token = tokens.token;
    const refresh = tokens.refresh;

    const tokenResult = await ctx.model.Token.create({
      token: new_token,
      refresh,
      code: token.code,
      valid: true,
    });

    if (tokenResult) {
      return {
        access_token: new_token,
        token_type: 'bearer',
        expires_in: 5 * 60,
        refresh_token: refresh,
        scope,
      };
    }

    return null;
  }
}

module.exports = TokenService;
